/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package httpserver.connections;

import com.sun.org.apache.xerces.internal.impl.dv.util.Base64;
import httpserver.util.AeSimpleSHA1;
import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.net.Socket;
import java.net.URLConnection;
import java.nio.ByteBuffer;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 *
 * @author Bryce
 */
public class Connection {

    private static int MASK_PREFIX_LEN = 2;
    private Socket socket;
    private String host;
    private String origin;
    private String key;
    private String websocketVersion;
    private String guid;
    private String returnKey;
    private PrintWriter output;
    private BufferedReader input;

    public Connection(Socket socket, String guid) {
        this.socket = socket;
        this.guid = guid;
        try {
            output = new PrintWriter(socket.getOutputStream(), true);
            input = new BufferedReader(new InputStreamReader(socket.getInputStream()));
        } catch (Exception e) {
            System.out.println("Error creating connection streams.. " + e);
        }

        System.out.println(socket.getInetAddress().getHostName() + " connected to server on " + socket.getPort() + ".\n");
    }

    public void sendMsg(String msg) {
        byte[] data = createWebsocketMask(msg);
        try {
            socket.getOutputStream().write(data);
            socket.getOutputStream().flush();
        } catch (Exception e) {
            System.out.println("Error sending message: " + e);
        }
    }

    public ArrayList<String> recData() {
        //We read in data line by line, checking for important data
        try {
            ArrayList<String> recLines = new ArrayList<String>();
            String line = input.readLine();
            while (line != null && line.length() > 0 && input.ready()) {
                recLines.add(line);
                line = input.readLine();
            }
            return recLines;
        } catch (Exception e) {
            System.out.println("Error reading data from client.. " + e);
            return null;
        }
    }

    public void acceptHandshake(ArrayList<String> header) {
        try {
            //now assemble the return key for the response.
            //String result = Base64.encode(AeSimpleSHA1.SHA1(key + guid).getBytes());
            String keyGuidCombo = key + guid;
            MessageDigest md = MessageDigest.getInstance("SHA-1");
            md.update(keyGuidCombo.getBytes(), 0, keyGuidCombo.length());
            byte[] result = md.digest();
            String finalResult = Base64.encode(result);
            //byte[] result = getHandshakeKey(returnKey, guid);

            System.out.println("SHA1 Result: " + finalResult + "\n");
            this.returnKey = finalResult;
        } catch (NoSuchAlgorithmException ex) {
            Logger.getLogger(Connection.class.getName()).log(Level.SEVERE, null, ex);
        }
        if (this.returnKey != null && this.returnKey.length() > 0) {
            String out = getWebsocketHeader();

            output.print(out);
            output.println();

            System.out.println("Response header sent: " + this.returnKey + "\n");
        }
    }

    private byte[] createWebsocketMask(String payload) {
        byte[] mask;
        ByteBuffer buffer = ByteBuffer.allocate(MASK_PREFIX_LEN + payload.getBytes().length);
        byte maskByte = -127;//first byte
        buffer.put(maskByte);
        maskByte = (byte) payload.getBytes().length;
        buffer.put(maskByte);
        buffer.put(payload.getBytes());//payload

        mask = buffer.array();
        return mask;
    }

    private String getWebsocketHeader() {
        return "HTTP/1.1 101 Switching Protocols\r\n"
                + "Upgrade: websocket\r\n"
                + "Connection: Upgrade\r\n"
                + "Sec-WebSocket-Accept: " + this.returnKey + "\r\n";
    }

    private static String convertToHex(byte[] data) {
        StringBuffer buf = new StringBuffer();
        for (int i = 0; i < data.length; i++) {
            int halfbyte = (data[i] >>> 4) & 0x0F;
            int two_halfs = 0;
            do {
                if ((0 <= halfbyte) && (halfbyte <= 9)) {
                    buf.append((char) ('0' + halfbyte));
                } else {
                    buf.append((char) ('a' + (halfbyte - 10)));
                }
                halfbyte = data[i] & 0x0F;
            } while (two_halfs++ < 1);
        }
        return buf.toString();
    }

    public byte[] getHandshakeKey(String firstKey, String secondKey) {
        byte[] toReturn = null;
        //Strip out numbers
        int firstNum = Integer.parseInt(firstKey.replaceAll("\\D", ""));
        int secondNum = Integer.parseInt(secondKey.replaceAll("\\D", ""));

        //Count spaces
        int firstDiv = firstKey.replaceAll("\\S", "").length();
        int secondDiv = secondKey.replaceAll("\\S", "").length();

        //Do the division
        int firstShake = firstNum / firstDiv;
        int secondShake = secondNum / secondDiv;

        //Prepare 128 bit byte array
        byte[] toMD5 = new byte[8];
        byte[] firstByte = ByteBuffer.allocate(4).putInt(firstShake).array();
        byte[] secondByte = ByteBuffer.allocate(4).putInt(secondShake).array();

        //Copy the bytes of the numbers you made into your md5 byte array
        System.arraycopy(firstByte, 0, toMD5, 0, 4);
        System.arraycopy(secondByte, 0, toMD5, 4, 4);
        try {
            //MD5 everything together
            MessageDigest md5 = MessageDigest.getInstance("MD5");
            toReturn = md5.digest(toMD5);
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }

        return toReturn;
    }

    private String assembleResponseHeader() {
        String resp = "HTTP/1.1 101 WebSocket Protocol Handshake\r\nUpgrade: WebSocket\r\nConnection: Upgrade\r\n"
                + "Sec-WebSocket-Accept: " + this.returnKey + "\r\n\r\n";
        return resp;
    }

    /**
     * @return the socket
     */
    public Socket getSocket() {
        return socket;
    }

    /**
     * @param socket the socket to set
     */
    public void setSocket(Socket socket) {
        this.socket = socket;
    }

    /**
     * @return the host
     */
    public String getHost() {
        return host;
    }

    /**
     * @param host the host to set
     */
    public void setHost(String host) {
        this.host = host;
    }

    /**
     * @return the origin
     */
    public String getOrigin() {
        return origin;
    }

    /**
     * @param origin the origin to set
     */
    public void setOrigin(String origin) {
        this.origin = origin;
    }

    /**
     * @return the key
     */
    public String getKey() {
        return key;
    }

    /**
     * @param key the key to set
     */
    public void setKey(String key) {
        this.key = key;
    }

    /**
     * @return the websocketVersion
     */
    public String getWebsocketVersion() {
        return websocketVersion;
    }

    /**
     * @param websocketVersion the websocketVersion to set
     */
    public void setWebsocketVersion(String websocketVersion) {
        this.websocketVersion = websocketVersion;
    }

    /**
     * @return the guid
     */
    public String getGuid() {
        return guid;
    }

    /**
     * @param guid the guid to set
     */
    public void setGuid(String guid) {
        this.guid = guid;
    }

    /**
     * @return the returnKey
     */
    public String getReturnKey() {
        return returnKey;
    }

    /**
     * @param returnKey the returnKey to set
     */
    public void setReturnKey(String returnKey) {
        this.returnKey = returnKey;
    }
}
